LiveData
是一個用於持有數據並可以監聽數據變動的元件,通常搭配 ViewModel 使用。
除此之外,LiveData
還可以感知生命週期,因此觀察者可以指定某一個具有生命週期的元件給 LiveData
,例如 Activity, Fragment,並在生命週期是 活躍
的時候進行更新,也就是生命週期裡的 onStart
和 onResume
。
除了傳統的觀察者模式中的功能之外,LiveData
還有很多優點:
LiveData
的任何事件的,能讓系統省去不必要的接受事件。LiveData
能夠感知生命週期,所以當 Activity 被銷毀時,LiveData
也會隨之銷毀,避免不必要的引用而造成的內存泄露。LiveData
則會讓應用處在 活躍狀態時 才跳 Toast,能帶來更好的使用者體驗。class MyViewModel : ViewModel() {
val name = MutableLiveData<String>()
}
只要把 ViewModel 裡面的數據全部改成用 MutableLiveData
包起來就可以了,MutableLiveData
裡面接的是泛型 T,所以可以接受任何的型態。
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activtiy_live_data)
viewModel = ViewModelProviders.of(this).get(MyViewModel::class.java)
viewModel.name.observe(this, object : Observer<String> {
override fun onChanged(name: String) {
userName.text = name// or do anything you want.
}
})
當 viewModel 裡面的變數 name 變動時,就會傳進 onChanged
,裡面再去實作數據變更後要做的事情就可以了。
這邊我曾經犯下一個錯誤 特地紀錄一下
我把 LiveData 和 DataBinding 一起使用的時候,因為 DataBinding 的 UI 更新是跟著 ViewModel 內的變數,所以我在 onChanged 裡面去改變 viewModel 裡面的值
viewModel.name.observe(this, object : Observer<String> {
override fun onChanged(name: String) {
viewModel.updateName(name)
}
})
這樣會造成什麼問題呢?因為 ViewModel 裡的 name 被改變了,在 View 這邊的 observe 觀察到他的改變,所以又進來 onChanged
這裏,又再把 ViewModel 裡的 name 更新了一次,View 這邊的 observe 又觀察到他的改變....
變成無窮迴圈了!
所以這邊其實可以把 observe 拿掉,讓 DataBinding 去更新,或是把 這個元件在 xml 的 binding 拿掉,然後在 onChanged
做正常的 settext,兩者擇一就可以了。
class MyViewModel : ViewModel() {
val name = MutableLiveData<String>()
fun updataName(name: String){
this.name.value = name// UI Thread
this.name.postValue(name)// Worker Thread
}
}
跟 DataBinding 一樣,在 View 那邊呼叫 updateName 把值丟進來,在 UI Thread 的更新可以直接 setValue
,在 Worker Thread 的更新就只能用 postValue
。
到這邊為止就算完成了 LiveData
的基本使用,非常的簡單!
如果今天 UI 接回來的 LiveData
不是自己想要的,而是想要做出一些組合,或是要將 LiveData
傳遞給另一個 LiveData
,就要用到 Transformations
這個類別。
舉例來說:
有一個 data class User 包在一個 LiveData
裡面
data class User(
val firstName: String,
val lastName: String
)
val user = MutableLiveData<User>()
如果我想要有一個 firstName + lastName 的組合,就可以這樣做:
val userFullName = Transformations.map(user) { user ->
user.firstName + user.lastName
}
大括號內取到這個 User 類別後,裡面的事就跟 LiveData 無關了,就可以去做一些自己想要的操作,操作完後返回的也是一個 LiveData
型別,所以要記得在 View 對這個 userFullName 做 observe 就可以了。
先來看這張圖
MediatorLiveData 是 LiveData 的子類,可以通過 MediatorLiveData 合併多個 LiveData 數據。其中任意一個 LiveData 數據發生變化,MediatorLiveData 都會通知觀察他的對象。
如果今天有一個需求是有很多項資料變動時都要做同一件事,比如說購物車內的任一商品有更動時、或是使用折價券時都要去更新總價,但又不想要在每個觀察事件內寫同樣的程式碼,這時候就可以使用 MediatorLiveData
來合併多個 LiveData
。
使用上也很簡單,宣告時的型態是 MediatorLiveData
,接著在適當的時機(通常是初始化時)把想要合併的 LiveData
利用 addSource
的方式加進去,並定義好當這個 LiveData
改變時,要做什麼事。
val name = MutableLiveData<String>()
val id = MutableLiveData<Int>()
val mediatorLiveData = MediatorLiveData<String>()
fun addSourceToMediatorLiveData() {
mediatorLiveData.addSource(name) {
//do something when name changed
}
mediatorLiveData.addSource(id) {
//do something when id changed
}
}
addSource
想要合併幾個都可以,記得在 View 觀察這個 mediatorLiveData 對象即可。
LiveData
篇幅不多,使用簡單,主要的功能其實都在跟 ViewModel 搭配的時候包在一起了,當然最大的賣點還是能夠感知生命週期,能夠讓 data 跟著 View 的生命週期在運作,就好像真的活生生的有生命一樣,重新定義了以前對數據類都是靜態的等著被呼叫、賦值的這種想法,使用了 LiveData
後,數據類變的動態,也更貼近了使用者,我想這也是 LiveData
的命名由來吧!
有任何問題或講得不清楚的地方歡迎留言和我討論。
更歡迎留言糾正我任何說錯的地方!
下一篇:Room (一) 介紹與基本使用